package org.activequant.math.algorithms;

import flanagan.math.Matrix;

/**
 * Exponential Moving Average accumulator.
 * Takes input as a series of measurements, and produces average signal by applying
 * EMA. 
 * <p>
 * <b>History:</b><br>
 *  - [22.02.2008] Created (Mike Kroutikov)<br>
 *
 *  @author Mike Kroutikov
 *  
 *  @see EMAAccumulator
 */
public class EMAMatrixAccumulator {
	private int period;
	private final Matrix value;
	private int numSamples;
	
	private double lambda = 0.0;
	private double correction = 1.; // small sample correction
	
	public EMAMatrixAccumulator(int nrow, int ncol) {
		value = new Matrix(nrow, ncol);
	}

	/**
	 * Averaging period (in "steps"). Measurements that are older than
	 * this does not contribute much to the output value.
	 * 
	 * @return averaging period.
	 */
	public int getPeriod() { 
		return period; 
	}
	/**
	 * Sets the averaging period.
	 * 
	 * @param val averaging period.
	 */
	public void setPeriod(int val) { 
		period = val; 
		lambda = 1. - 1. / period;
	}

	/**
	 * Computed average. This value gets re-computed after every measurement.
	 * 
	 * @return computed value.
	 */
	public Matrix getMeanValue() {
		return value; 
	}

	/**		Matrix.minus(input, value).timesEquals((1. - lambda) / (1. - correction)));

	 * Total number of samples that this accumulator received.
	 * This value is recomputed after every measurement. One can check this and
	 * compare with the {@link #getPeriod() period} to get an idea if
	 * the accumulator has enough data to produce meaningful average.
	 * 
	 * @return total length.
	 */
	public int getNumSamples() { return numSamples; }
	
	/**
	 * Call this to pass next measurement.
	 * After this function returns, following
	 * properties will be recomputed: {@link #getNumSamples() numSamples},
	 * {@link #getMeanValue() meanValue}.
	 * 
	 * @param input value of the signal.
	 */
	public void accumulate(Matrix input) {
		correction *= lambda;

		Matrix innovation = Matrix.minus(input, value);
		innovation.timesEquals((1. - lambda) / (1. - correction));
		value.plusEquals(innovation);
		
		numSamples ++;
	}
	
	public String toString() {
		return "" + value + "(" + numSamples + " samples)";
	}
}
